package com.wumz.spring.write.v2.framework.context;

import com.wumz.spring.write.v2.framework.annotation.MyAutowired;
import com.wumz.spring.write.v2.framework.annotation.MyController;
import com.wumz.spring.write.v2.framework.annotation.MyService;
import com.wumz.spring.write.v2.framework.aop.config.MyAopConfig;
import com.wumz.spring.write.v2.framework.aop.proxy.MyJdkDynamicAopProxy;
import com.wumz.spring.write.v2.framework.aop.support.MyAdvisedSupport;
import com.wumz.spring.write.v2.framework.beans.MyBeanWrapper;
import com.wumz.spring.write.v2.framework.beans.config.MyBeanDefinitionReader;
import com.wumz.spring.write.v2.framework.beans.support.MyBeanDefinition;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

/**
 * Created by wumz on 2020/9/28 10:10
 */
public class MyApplicationContext {

    private MyBeanDefinitionReader reader;

    private Map<String, MyBeanDefinition> beanDefinitionMap = new HashMap<String, MyBeanDefinition>();

    //bean的实例缓存 <全类名, 类实例>
    private Map<String, Object> factoryBeanObjectCache = new HashMap<String, Object>();

    //bean的包装器缓存 <全类名. 包装器>
    private Map<String, MyBeanWrapper> factoryBeanInstanceCache = new HashMap<String, MyBeanWrapper>();

    public MyApplicationContext(String... configLocations) {
        try {

            //1、加载配置文件
            reader = new MyBeanDefinitionReader(configLocations);

            //2、解析配置文件，封装成BeanDefinition, 保存的是全类名
            List<MyBeanDefinition> beanDefinitions = reader.loadBeanDefinitions();

            //3、把BeanDefintion缓存起来
            doRegistBeanDefinition(beanDefinitions);

            //4、DI赋值，扫描ioc容器中的实例，给没有赋值的属性自动赋值
            doAutowrited();
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     * DI赋值，扫描ioc容器中的实例，给没有赋值的属性自动赋值
     */
    private void doAutowrited() {
        //调用getBean()
        //这一步，所有的Bean并没有真正的实例化，还只是配置阶段
        for (Map.Entry<String, MyBeanDefinition> entry : this.beanDefinitionMap.entrySet()) {
            String beanName = entry.getKey();
            //Bean的实例化，DI是从而这个方法开始的
            getBean(beanName);
        }
    }

    /**
     * Bean的实例化，DI是从而这个方法开始的
     * @param beanName
     */
    public Object getBean(String beanName) {
        //1. 先拿到BeanDefinition配置信息
        MyBeanDefinition myBeanDefinition = this.beanDefinitionMap.get(beanName);

        //2. 反射实例化,创建真正的实例对象
        Object instance = instantiateBean(beanName, myBeanDefinition);

        //3. 封装bean的包装器
        MyBeanWrapper myBeanWrapper = new MyBeanWrapper(instance);

        //4. 保存到IoC容器
        this.factoryBeanInstanceCache.put(beanName, myBeanWrapper);

        //5. 执行依赖注入
        populateBean(beanName,myBeanDefinition, myBeanWrapper);
        return myBeanWrapper.getWrapperInstance();
    }

    /**
     * 执行依赖注入
     * @param beanName
     * @param myBeanDefinition
     * @param myBeanWrapper
     */
    private void populateBean(String beanName, MyBeanDefinition myBeanDefinition, MyBeanWrapper myBeanWrapper) {
        //可能涉及到循环依赖？
        //A{ B b}
        //B{ A b}
        //用两个缓存，循环两次
        //1、把第一次读取结果为空的BeanDefinition存到第一个缓存
        //2、等第一次循环之后，第二次循环再检查第一次的缓存，再进行赋值
        Object instance = myBeanWrapper.getWrapperInstance();
        Class<?> clazz = myBeanWrapper.getWrapperClazz();
        //在Spring中@Component
        if(!(clazz.isAnnotationPresent(MyController.class) || clazz.isAnnotationPresent(MyService.class))){
            return;
        }
        //把所有的包括private/protected/default/public 修饰字段都取出来
        for (Field field : clazz.getFields()) {
            if (!field.isAnnotationPresent(MyAutowired.class)){
                continue;
            }
            MyAutowired annotation = field.getAnnotation(MyAutowired.class);
            //如果用户没有自定义的beanName，就默认根据类型注入
            String autoBeanName = annotation.value().trim();
            if ("".equals(autoBeanName)){
                autoBeanName = field.getType().getName();
            }
            //暴力访问
            field.setAccessible(true);
            try {
                if (!factoryBeanInstanceCache.containsKey(autoBeanName)){
                    continue;
                }
                //ioc.get(beanName) 相当于通过接口的全名拿到接口的实现的实例
                field.set(instance, this.factoryBeanInstanceCache.get(autoBeanName).getWrapperInstance());
            }catch (Exception e){
                e.printStackTrace();
                continue;
            }
        }
    }

    /**
     * 反射实例化,创建真正的实例对象
     * @param beanName
     * @param myBeanDefinition
     * @return
     */
    private Object instantiateBean(String beanName, MyBeanDefinition myBeanDefinition) {
        String className = myBeanDefinition.getBeanClassName();
        Object instance = null;
        try {
            if (this.factoryBeanObjectCache.containsKey(beanName)){
                instance = factoryBeanObjectCache.get(beanName);
                return instance;
            }
            Class<?> clazz = Class.forName(className);
            //默认的类名首字母小写
            instance = clazz.newInstance();
            //==================AOP开始=========================
            //如果满足条件，就直接返回Proxy对象
            //1、加载AOP的配置文件
            MyAdvisedSupport config = instantionAopConfig(myBeanDefinition);
            config.setTargetClass(clazz);
            config.setTarget(instance);

            //判断规则，要不要生成代理类，如果要就覆盖原生对象
            //如果不要就不做任何处理，返回原生对象
            if(config.pointCutMath()){
                instance = new MyJdkDynamicAopProxy(config).getProxy();
            }
            //===================AOP结束========================
            this.factoryBeanObjectCache.put(beanName, instance);
        }catch (Exception e){
            e.printStackTrace();
        }
        return instance;
    }

    /**
     * 加载AOP的配置文件
     * @param myBeanDefinition
     * @return
     */
    private MyAdvisedSupport instantionAopConfig(MyBeanDefinition myBeanDefinition) {
        MyAopConfig config = new MyAopConfig();
        config.setPointCut(this.reader.getConfig().getProperty("pointCut"));
        config.setAspectClass(this.reader.getConfig().getProperty("aspectClass"));
        config.setAspectBefore(this.reader.getConfig().getProperty("aspectBefore"));
        config.setAspectAfter(this.reader.getConfig().getProperty("aspectAfter"));
        config.setAspectAfterThrow(this.reader.getConfig().getProperty("aspectAfterThrow"));
        config.setAspectAfterThrowingName(this.reader.getConfig().getProperty("aspectAfterThrowingName"));
        return new MyAdvisedSupport(config);
    }

    /**
     * 把BeanDefintion缓存起来
     * @param beanDefinitions
     */
    private void doRegistBeanDefinition(List<MyBeanDefinition> beanDefinitions) throws Exception {
        for (MyBeanDefinition beanDefinition : beanDefinitions) {
            if (this.beanDefinitionMap.containsKey(beanDefinition.getFactoryBeanName())){
                throw new Exception("The " + beanDefinition.getFactoryBeanName() + "is exists");
            }

            //put两次是为了可以既通过类名找到这个bean，也可以通过默认首字母小写的beanName找到这个bean
            beanDefinitionMap.put(beanDefinition.getFactoryBeanName(), beanDefinition);
            beanDefinitionMap.put(beanDefinition.getBeanClassName(), beanDefinition);
        }
    }

    public int getBeanDefinitionCount() {
        return this.beanDefinitionMap.size();
    }

    public String[] getBeanDefinitionNames() {
        return this.beanDefinitionMap.keySet().toArray(new String[this.beanDefinitionMap.size()]);
    }

    public Properties getConfig() {
        return this.reader.getConfig();
    }
}
